package com.big.cart.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.big.cart.service.CartService;
import com.big.cmn.DictFeignClient;
import com.big.common.exception.BigException;
import com.big.common.result.Result;
import com.big.common.result.ResultCodeEnum;
import com.big.common.utils.JsonUtils;
import com.big.merchandise.MerchandiseFeignClient;
import com.big.model.model.merchandise.MerchandiseDetail;
import com.big.model.model.merchandise.MerchandiseInfo;
import com.big.model.model.merchandise.MerchandiseOffers;
import com.big.model.model.merchandise.MerchandiseSpecificationPackage;
import com.big.model.model.patient.PatientInfo;
import com.big.model.vo.cart.RedisCartVo;
import com.big.patient.PatientFeignClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@Transactional(rollbackFor=Exception.class)
@Slf4j
public class CartServiceImpl implements CartService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private MerchandiseFeignClient merchandiseFeignClient;

    @Autowired
    private PatientFeignClient patientFeignClient;

    @Autowired
    private DictFeignClient dictFeignClient;

    /**
     *  添加购物车
     *
     *  加入Redis分布式事务锁
     *
     * value：抛出指定异常才会重试
     * include：和value一样，默认为空，当exclude也为空时，默认所有异常
     * exclude：指定不处理的异常
     * maxAttempts：最大重试次数，默认3次
     * backoff：重试等待策略，默认使用@Backoff，@Backoff的value默认为1000L，我们设置为2000L；multiplier（指定延迟倍数）默认为0，表示固定暂停1秒后进行重试，如果把multiplier设置为1.5，则第一次重试为2秒，第二次为3秒，第三次为4.5秒
     */
    @Override
//    @Retryable(value = Exception.class,maxAttempts = 3,backoff = @Backoff(delay = 2000,multiplier = 1.5))
    public boolean AddCart(RedisCartVo redisCartVo) {
//        状态标记
        boolean flag = false;
            //重新写入数据
            MerchandiseDetail merchandiseDetail = merchandiseFeignClient.FindMerchanInfo(String.valueOf(redisCartVo.getMid()));
            //TODO 写入实体类
            redisCartVo.setImage(merchandiseDetail.getDImage());
            redisCartVo.setMid(String.valueOf(merchandiseDetail.getDId()));
            //判断数据是否空值
            if (StringUtils.isEmpty(redisCartVo.getMid())  && StringUtils.isEmpty(redisCartVo.getName()) && StringUtils.isEmpty(redisCartVo.getNum()) && StringUtils.isEmpty(redisCartVo.getImage()) && StringUtils.isEmpty(redisCartVo.getUid()) && StringUtils.isEmpty(redisCartVo.getPrice())) {
                throw new BigException(ResultCodeEnum.DATA_ERROR);
            }
            //获取前台数据 id和小key
            String UserId = String.valueOf(redisCartVo.getUid());
            String MerchanCode = redisCartVo.getMid()+redisCartVo.getSpackage();
            MerchandiseSpecificationPackage specificationPackage = merchandiseFeignClient.findSpecificationPackage(redisCartVo.getMid());
            System.out.println("数据"+specificationPackage.getParam().toString());
            //配置信息
            redisCartVo.getParam().put("cpu",specificationPackage.getParam().get("cpuName"));
            redisCartVo.getParam().put("hdd",specificationPackage.getParam().get("hddName"));
            redisCartVo.getParam().put("gpu",specificationPackage.getParam().get("gpuName"));
            redisCartVo.getParam().put("run",specificationPackage.getParam().get("runMemoryName"));
//            redisCartVo.setImage(specificationPackage.getImage());
            // ToDo 在没添加商品之前获取所有的keys的数量
            int hashLengthQ = redisTemplate.opsForHash().keys(UserId).size();
            Map<String, Object> map = new HashMap<>();
            // ToDo  查看是否第一次加入购物车
            List<Object> hashList = redisTemplate.opsForHash().values(UserId);
            if (hashList.size() > 0) {//不为空,取值
                Map<String, Object> resultMap = this.resultMap(redisCartVo,"正常",null);
                map=resultMap;
                // ToDo 查看用户是否有此商品，
                Set<String> setKyes = redisTemplate.opsForHash().keys(UserId);
                for (String keys : setKyes) {//循环用户所有的商品编号
                    if (keys.equals(MerchanCode)) {//已存在此商品
                        //TODO 查找套餐组
                        //获取商品信息,累加数量
                        Object count = redisTemplate.opsForHash().get(UserId, MerchanCode);
                        //object 转class
                        String StringInfo = JSONObject.toJSONString(count);
                        RedisCartVo c = JSONObject.parseObject(StringInfo, RedisCartVo.class);
                        if(c.getSpackage().equals(redisCartVo.getSpackage())){ //规格相同的情况下进行累加,如果不同,新增缓存
                        // ToDo 接收前台数据累加redis商品数量
                        Integer number = (c.getNum() + redisCartVo.getNum());
                        // TODO 覆盖当前商品的数量
                        Map<String, Object> resultMapChange = this.resultMap(redisCartVo,"改变",number);
                        map=resultMapChange;
                        redisTemplate.opsForHash().put(UserId, MerchanCode, map);
                        flag = true;
                        }else {
                            Map<String, Object> resultMapChange = this.resultMap(redisCartVo,"正常",null);
                            map=resultMapChange;
                            redisTemplate.opsForHash().put(UserId, MerchanCode, map);
                            flag = true;
                        }
                    }
                }
            } else {//为空, 用户里没有任何一件商品
                Map<String, Object> resultMap = this.resultMap(redisCartVo,"正常",null);
                map=resultMap;
            }
            redisTemplate.opsForHash().put(UserId, MerchanCode, map);
            // ToDo  返回redis最新key数量
            int hashLengthH = redisTemplate.opsForHash().keys(UserId).size();
            //ToDo 是否添加完成
            if (hashLengthH - hashLengthQ > 0) {
                flag = true;
            }

         return  flag;
    }



    //购物车列表
    @Override
    public List<RedisCartVo> showCart(Long uid) {
        PatientInfo patientInfo=patientFeignClient.FindPatient(String.valueOf(uid));
        Set<String> setKyes =redisTemplate.opsForHash().keys(String.valueOf(patientInfo.getUserId()));
        List<RedisCartVo>list=new ArrayList<>();
        for (String keys : setKyes) {//循环用户所有的商品编号
            Object count =  redisTemplate.opsForHash().get(String.valueOf(patientInfo.getUserId()),keys);
            //object 转class
            String StringInfo=JSONObject.toJSONString(count);
            RedisCartVo c=JSONObject.parseObject(StringInfo,RedisCartVo.class);
            list.add(c);
        }
        return list;
    }


    /**
     * 修改选中状态
     */
    @Override
    public boolean upStatusCart(RedisCartVo redisCartVo)  {
        Map<String,Object> map=new HashMap<>();
        //判断数据是否空值
        if(StringUtils.isEmpty(redisCartVo.getMid())
                &&StringUtils.isEmpty(redisCartVo.getName())
                &&StringUtils.isEmpty(redisCartVo.getNum())
                &&StringUtils.isEmpty(redisCartVo.getImage())
                &&StringUtils.isEmpty(redisCartVo.getUid())
                &&StringUtils.isEmpty(redisCartVo.getPrice())){
            throw  new BigException(ResultCodeEnum.DATA_ERROR);
        }
        /**
         * 改变状态
         */
        //先获取具体用户商品信息
        String UserId = String.valueOf(redisCartVo.getUid());
        String MerchanCode = String.valueOf(redisCartVo.getMid());
        Object count =  redisTemplate.opsForHash().get(UserId,MerchanCode);
        //object 转class
        String StringInfo=JSONObject.toJSONString(count);
        RedisCartVo c=JSONObject.parseObject(StringInfo,RedisCartVo.class);
        map.put("mid", c.getMid());
        map.put("uid", c.getUid());
        map.put("name", c.getName());
        map.put("price", c.getPrice());
        if(redisCartVo.getNum()!=null ||redisCartVo.getNum()!=0){
            map.put("num", redisCartVo.getNum());
        }else {
            map.put("num", c.getNum());
        }
        map.put("image", c.getImage());
        map.put("check", redisCartVo.isCheck());
        map.put("total",c.getPrice()*redisCartVo.getNum());
        map.put("offers", this.offersList(c,c.getNum()));
        map.put("spackage",c.getSpackage());
        redisTemplate.opsForHash().put(UserId,MerchanCode,map);
        return true;
    }


    /**
     * 删除
     * @param cartVo
     * @return
     */
    @Override
    public boolean deCart(RedisCartVo cartVo) {
        log.info("值{}",cartVo);
        //判断数据是否空值
        if(StringUtils.isEmpty(cartVo.getMid())
                &&StringUtils.isEmpty(cartVo.getUid())){
            throw  new BigException(ResultCodeEnum.DATA_ERROR);
        }
        //TODO 删除
        cartVo.setUid(String.valueOf(cartVo.getUid()));
        redisTemplate.opsForHash().delete(String.valueOf(cartVo.getUid()),String.valueOf(cartVo.getMid()+cartVo.getSpackage()));
        return true;

    }


    /**
     * 优惠方式名称
     */
    public MerchandiseOffers offerName (MerchandiseOffers merchandiseOffers){
        String name = dictFeignClient.getName(String.valueOf(merchandiseOffers.getOfferCode()));
        merchandiseOffers.getParam().put("offName",name);
        return merchandiseOffers;
    }


    /**
     * 优惠方式
     * @param redisCartVo
     * @param number
     * @return
     */
    public  List<MerchandiseOffers> offersList(RedisCartVo redisCartVo, Integer number){
        List<MerchandiseOffers>offersList=new ArrayList<>();
        Result result = merchandiseFeignClient.offersInfos(redisCartVo.getMid());
        List<MerchandiseOffers> merchandiseOffers = JsonUtils.jsonToList(JSON.toJSONString(result.getData()), MerchandiseOffers.class);
        merchandiseOffers.forEach(item -> {
            this.offerName(item);

            //金额结果
            if(item.getOfferCode()==20000001){ //满减2000001
                //规则拦截
                double this_total=number*redisCartVo.getPrice();
                if(this_total>=item.getFullReductionRules()){ //符合规则
                    offersList.add(item);
                    item.getParam().put("FullSubtractionResult",this_total-item.getFullReductionDeduction());
                }
            }
            if(item.getOfferCode()==20000002){ //阶梯 20000002
                //规则拦截
                Integer this_number=number;
                double this_total=number*redisCartVo.getPrice();
                if(this_number>=item.getFullReductionRules()){ //符合规则
                    offersList.add(item);
                    MerchandiseInfo infoMerchandise = merchandiseFeignClient.findInfoMerchandise(redisCartVo.getMid());
                    double originalPrice = infoMerchandise.getOriginalPrice();
                    //折扣  折扣＝现价／原价×100%
                    double ladder = ((originalPrice*item.getFullReductionDeduction())/originalPrice * 100 );
                    BigDecimal BigLadder   =   new   BigDecimal(ladder);
                    item.getParam().put("LadderResults",this_total-ladder);
                }
            }
        });
        return offersList;
    }

    /**
     * 返回Map
     */

    public Map<String, Object> resultMap(RedisCartVo redisCartVo,String changeString,Integer number){
        Map<String, Object> map = new HashMap<>();
        if(changeString.equals("正常")){
        map.put("mid", String.valueOf(redisCartVo.getMid()));
        map.put("uid", String.valueOf(redisCartVo.getUid()));
        map.put("name", redisCartVo.getName());
        map.put("price", redisCartVo.getPrice());
        map.put("num", redisCartVo.getNum());
        map.put("image", redisCartVo.getImage());
        map.put("check", false);
        map.put("total", redisCartVo.getNum()*redisCartVo.getPrice());
        map.put("spackage",redisCartVo.getSpackage());
        map.put("spacParam",redisCartVo.getParam());

        }else  if (changeString.equals("改变")&&number!=null){
            map.put("mid", String.valueOf(redisCartVo.getMid()));
            map.put("uid", String.valueOf(redisCartVo.getUid()));
            map.put("name", redisCartVo.getName());
            map.put("price", redisCartVo.getPrice());
            map.put("num", number);
            map.put("image", redisCartVo.getImage());
            map.put("check", false);
            map.put("total",number*redisCartVo.getPrice());
            map.put("spackage",redisCartVo.getSpackage());
            map.put("spacParam",redisCartVo.getParam());

        }
        return map;
    }



}
